// mp3player.c: MP3 Player Implementation in C for Sony PSP
//
////////////////////////////////////////////////////////////////////////////

#include <pspkernel.h>
#include <pspiofilemgr.h>
#include <pspdisplay.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <pspaudiolib.h>
#include "mp3player.h"


#define FALSE 0
#define TRUE !FALSE
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
#define MadErrorString(x) mad_stream_errorstr(x)
#define INPUT_BUFFER_SIZE	(5*8192)

#include <pspdebug.h>
#ifdef WANT_AUDIO_DEBUG
	
	/* Define printf, just to make typing easier */
	#define printf	pspDebugScreenPrintf

	static int PrintFrameInfo(struct mad_header *Header)
	{
		const char *Layer, *Mode, *Emphasis;

		/* Convert the layer number to it's printed representation. */
		switch (Header->layer) {
		case MAD_LAYER_I:
		Layer = "I";
		break;
		case MAD_LAYER_II:
		Layer = "II";
		break;
		case MAD_LAYER_III:
		Layer = "III";
		break;
		default:
		Layer = "(unexpected layer value)";
		break;
		}

		/* Convert the audio mode to it's printed representation. */
		switch (Header->mode) {
		case MAD_MODE_SINGLE_CHANNEL:
		Mode = "single channel";
		break;
		case MAD_MODE_DUAL_CHANNEL:
		Mode = "dual channel";
		break;
		case MAD_MODE_JOINT_STEREO:
		Mode = "joint (MS/intensity) stereo";
		break;
		case MAD_MODE_STEREO:
		Mode = "normal LR stereo";
		break;
		default:
		Mode = "(unexpected mode value)";
		break;
		}

		/* Convert the emphasis to it's printed representation. Note that
		 * the MAD_EMPHASIS_RESERVED enumeration value appeared in libmad
		 * version 0.15.0b.
		 */
		switch (Header->emphasis) {
		case MAD_EMPHASIS_NONE:
		Emphasis = "no";
		break;
		case MAD_EMPHASIS_50_15_US:
		Emphasis = "50/15 us";
		break;
		case MAD_EMPHASIS_CCITT_J_17:
		Emphasis = "CCITT J.17";
		break;
	#if (MAD_VERSION_MAJOR>=1) || \
	  ((MAD_VERSION_MAJOR==0) && (MAD_VERSION_MINOR>=15))
		case MAD_EMPHASIS_RESERVED:
		Emphasis = "reserved(!)";
		break;
	#endif
		default:
		Emphasis = "(unexpected emphasis value)";
		break;
		}
		pspDebugScreenSetXY(0, 29);
		//printf("%lu kb/s audio MPEG layer %s stream at %dHz\n", Header->bitrate / 1000, Layer, Header->samplerate);
		sceDisplayWaitVblankStart();
		return (0);
	}

#endif

KMp3Stream::KMp3Stream(const char* filename)
{
	mStreamPtr = NULL;
	mStreamSize = 0;
	mIsOk = false;
	int fd;
    //psp_stats pstat;
    //sceIoGetstat(filename, &pstat);
    if ((fd = sceIoOpen(filename, PSP_O_RDONLY, 0777)) > 0) {
		//  opened file, so get size now
		mStreamSize = sceIoLseek(fd, 0, PSP_SEEK_END);
		sceIoLseek(fd, 0, PSP_SEEK_SET);
		mStreamPtr = (unsigned char *) malloc(mStreamSize + 8);
		memset(mStreamPtr, 0, mStreamSize + 8);
		if (mStreamPtr != 0) {		// Read file in
			sceIoRead(fd, mStreamPtr, mStreamSize);
		} else {
			//printf("Error allocing\n");
			sceIoClose(fd);
			return;
		}
		
		// Close file
		sceIoClose(fd);
		mIsOk = true;
	} 
}

KMp3Stream::~KMp3Stream()
{
    if (mStreamPtr)
		free(mStreamPtr);
}

KMp3Decoder::KMp3Decoder()
{
	mSamplesInOutput = 0;
	mOutputBufferEnd = (unsigned char *) mOutputBuffer + OUTPUT_BUFFER_SIZE * 2;
	mStatus = 0;
	mCurrentFrameIndex = 0;
	mOutputPtr = (unsigned char *) mOutputBuffer, 
	mGuardPtr = NULL;
	mStreamPtr = NULL;
	
	mIsPlaying = false;
	mIsPaused = false;
}

/****************************************************************************
* Converts a sample from libmad's fixed point number format to a signed	*
* short (16 bits).															*
****************************************************************************/
signed short KMp3Decoder::MadFixedToSshort(mad_fixed_t Fixed)
{
    /* A fixed point number is formed of the following bit pattern:
     *
     * SWWWFFFFFFFFFFFFFFFFFFFFFFFFFFFF
     * MSB                          LSB
     * S ==> Sign (0 is positive, 1 is negative)
     * W ==> Whole part bits
     * F ==> Fractional part bits
     *
     * This pattern contains MAD_F_FRACBITS fractional bits, one
     * should alway use this macro when working on the bits of a fixed
     * point number. It is not guaranteed to be constant over the
     * different platforms supported by libmad.
     *
     * The signed short value is formed, after clipping, by the least
     * significant whole part bit, followed by the 15 most significant
     * fractional part bits. Warning: this is a quick and dirty way to
     * compute the 16-bit number, madplay includes much better
     * algorithms.
     */

    /* Clipping */
    if (Fixed >= MAD_F_ONE)
	return (SHRT_MAX);
    if (Fixed <= -MAD_F_ONE)
	return (-SHRT_MAX);

    /* Conversion. */
    Fixed = Fixed >> (MAD_F_FRACBITS - 15);
    return ((signed short) Fixed);
}

bool KMp3Decoder::MP3_Decode(void* buffer, unsigned int numSamples, bool mixPCM)
{
	signed short *_buf = (short *)buffer;
    unsigned long samplesOut = 0;
    //      u8 justStarted = 1;

    if (mIsPlaying && !mIsPaused) {	
		//  Playing , so mix up a buffer
		if (mSamplesInOutput > 0) {
			//printf("%d samples in buffer\n", mSamplesInOutput);
			if ((unsigned int)mSamplesInOutput > numSamples) {
				if (mixPCM) {
					for (int count = 0; count < numSamples * 2; count++)
						*(_buf + count) += mOutputBuffer[count];
				}
				else
					memcpy((char *) _buf, (char *) mOutputBuffer, numSamples * 2 * 2);
					
				samplesOut = numSamples;
				mSamplesInOutput -= numSamples;
			} 
			else {
				if (mixPCM) {
					for (int count = 0; count < mSamplesInOutput * 2; count++)
						*(_buf + count) += mOutputBuffer[count];
				}
				else
					memcpy((char *) _buf, (char *) mOutputBuffer, mSamplesInOutput * 2 * 2);
					
				samplesOut = mSamplesInOutput;
				mSamplesInOutput = 0;
			}
		}

		while (samplesOut < numSamples) {
			if (mStream.buffer == NULL || mStream.error == MAD_ERROR_BUFLEN) {

			mStream.error = MAD_ERROR_NONE;
			}

			if (mad_frame_decode(&mFrame, &mStream)) {
			if (MAD_RECOVERABLE(mStream.error)) {
				/* Do not print a message if the error is a loss of
				 * synchronization and this loss is due to the end of
				 * stream guard bytes. (See the comments marked {3}
				 * supra for more informations about guard bytes.)
				 */
				if (mStream.error != MAD_ERROR_LOSTSYNC || mStream.this_frame != mGuardPtr) {
					//printf("recoverable frame level error (%s)\n", MadErrorString(&mStream));
				}
				return false;	//continue;
			} else if (mStream.error == MAD_ERROR_BUFLEN) {
				mIsEOS = 1;
				return false;	//continue;
			} else {
				//printf("unrecoverable frame level error (%s).\n", MadErrorString(&mStream));
				mStatus = 1;
				MP3_Stop();	//break;
			}
			}

			/* The characteristics of the stream's first frame is printed
			 * on stderr. The first frame is representative of the entire
			 * stream.
			 */
			if (mCurrentFrameIndex == 0) {
	#ifdef WANT_AUDIO_DEBUG
				if (PrintFrameInfo(&mFrame.header)) {
					mStatus = 1;
					//break;
				}
	#endif
			}

			/* Accounting. The computed frame duration is in the frame
			 * header structure. It is expressed as a fixed point number
			 * whole data type is mad_timer_t. It is different from the
			 * samples fixed point format and unlike it, it can't directly
			 * be added or subtracted. The timer module provides several
			 * functions to operate on such numbers. Be careful there, as
			 * some functions of libmad's timer module receive some of
			 * their mad_timer_t arguments by value!
			 */
			mCurrentFrameIndex++;
			mad_timer_add(&mTimer, mFrame.header.duration);

			/* Once decoded the frame is synthesized to PCM samples. No errors
			 * are reported by mad_synth_frame();
			 */
			mad_synth_frame(&mSynth, &mFrame);

			/* Synthesized samples must be converted from libmad's fixed
			 * point number to the consumer format. Here we use unsigned
			 * 16 bit big endian integers on two channels. Integer samples
			 * are temporarily stored in a buffer that is flushed when
			 * full.
			 */

			for (int i = 0; i < mSynth.pcm.length; i++) {
				signed short Sample;
				//printf("%d < %d\n", samplesOut, numSamples);
				if (samplesOut < numSamples) {
					//printf("I really get here\n");
					/* Left channel */
					Sample = MadFixedToSshort(mSynth.pcm.samples[0][i]);
				
					if (mixPCM)
						_buf[samplesOut * 2] += Sample;
					else
						_buf[samplesOut * 2] = Sample;

					/* Right channel. If the decoded stream is monophonic then
					 * the right output channel is the same as the left one.
					 */
					if (MAD_NCHANNELS(&mFrame.header) == 2)
					Sample = MadFixedToSshort(mSynth.pcm.samples[1][i]);
					if (mixPCM)
						_buf[samplesOut * 2 + 1] += Sample;
					else
						_buf[samplesOut * 2 + 1] = Sample;
					samplesOut++;
				} else {
					//printf("%d < %d of %d\n", samplesOut, numSamples, mSynth.pcm.length);
					Sample = MadFixedToSshort(mSynth.pcm.samples[0][i]);
					mOutputBuffer[mSamplesInOutput * 2] = Sample;
					//mOutputBuffer[mSamplesInOutput*4+1]=0;//Sample>>8;
					//mOutputBuffer[mSamplesInOutput*4+2]=0;//Sample&0xff;
					if (MAD_NCHANNELS(&mFrame.header) == 2)
					Sample = MadFixedToSshort(mSynth.pcm.samples[1][i]);
					mOutputBuffer[mSamplesInOutput * 2 + 1] = Sample;
					//mOutputBuffer[mSamplesInOutput*4+3]=0;//Sample>>8;
					//mOutputBuffer[mSamplesInOutput*4+4]=0;//Sample&0xff;
					mSamplesInOutput++;
				}

			}
		}
    } 
	else {			//  Not Playing , so clear buffer
		if (!mixPCM) {
			unsigned int count;
			for (count = 0; count < numSamples * 2; count++)
				*(_buf + count) = 0;
		}
    }
	
	return true;
}


int KMp3Decoder::MP3_Load(const KMp3Stream& stream)
{
	mIsEOS = 0;
	mIsPlaying = true;
	mIsPaused = true;
	mCurrentFrameIndex = 0;
	mad_timer_reset(&mTimer);
	
	if (!stream.mIsOk)
		return 0;
	
	mStreamPtr = &stream;
	
	mCurrentFrameIndex = 0;
    /* First the structures used by libmad must be initialized. */
    mad_stream_init(&mStream);
    mad_frame_init(&mFrame);
    mad_synth_init(&mSynth);
	// Pipe the new buffer content to libmad's stream decoder facility.
	mad_stream_buffer(&mStream, mStreamPtr->mStreamPtr, mStreamPtr->mStreamSize);
		
    return 1;
}

void KMp3Decoder::MP3_Free()
{
	if (mStreamPtr) {
		MP3_Stop();
		/* Mad is no longer used, the structures that were initialized must
		 * now be cleared.
		 */
		mad_synth_finish(&mSynth);
		mad_frame_finish(&mFrame);
		mad_stream_finish(&mStream);

		/* Accounting report if no error occurred. */
		if (!mStatus) {
			//char Buffer[80];
			//mad_timer_string(mTimer, Buffer, "%lu:%02lu.%03u", MAD_UNITS_MINUTES, MAD_UNITS_MILLISECONDS, 0);
			//printf("%lu frames decoded (%s).\n", mCurrentFrameIndex, Buffer);
			//sceKernelDelayThread(500000);
		}
	}
	
	mStreamPtr = NULL;
}


// This function initialises for playing, and starts
bool KMp3Decoder::MP3_Play()
{
    mIsPaused = false;
    return true;
}

void KMp3Decoder::MP3_Pause()
{
    mIsPaused = true;
}

void KMp3Decoder::MP3_Stop()
{
    //stop playing
    mIsPlaying = false;

    //clear buffer
    memset(mOutputBuffer, 0, OUTPUT_BUFFER_SIZE);
    mOutputPtr = (unsigned char *) mOutputBuffer;
}

void KMp3Decoder::MP3_GetTimeString(char *dest)
{
    mad_timer_string(mTimer, dest, "%02lu:%02u:%02u", MAD_UNITS_HOURS, MAD_UNITS_MILLISECONDS, 0);
}

int KMp3Decoder::MP3_EndOfStream()
{
    if (mIsEOS == 1)
		return 1;
	else
		return 0;
}

bool KMp3Decoder::IsPlaying()
{
	return mIsPlaying;
}

KAudioChannel::KAudioChannel(int channel)
{
	//pspAudioSetChannelCallback(mChannel, 0, 0);
	
    mChannel = channel;
	pspAudioSetChannelCallback(mChannel, AudioCallback, this);
	for (int i = 0; i < MAX_SOFT_MIX_PER_CH; ++i)
		mOccupiedMask[i] = 0;
}

KAudioChannel::~KAudioChannel()
{
}

void KAudioChannel::AudioCallback(void *_buf2, unsigned int numSamples, void *pdata)
{
	KAudioChannel* pChannel = (KAudioChannel*)pdata;
	int i = 0;
	for (; i < MAX_SOFT_MIX_PER_CH; ++i) {
		if (pChannel->mStreamDecoder[i].IsPlaying()) {
			pChannel->mStreamDecoder[i].MP3_Decode(_buf2, numSamples, false);
			i++;
			break;
		}
	}
	
	for (; i < MAX_SOFT_MIX_PER_CH; ++i) {
		if (pChannel->mStreamDecoder[i].IsPlaying()) {
			pChannel->mStreamDecoder[i].MP3_Decode(_buf2, numSamples, true);
		}
	}
	
}
	
void KAudioChannel::PlayStream(const KMp3Stream& stream)
{
	int decoderIdx = -1;
	for (int i = 0; i < MAX_SOFT_MIX_PER_CH; ++i) {
		if (mOccupiedMask[i] == 0) {
			decoderIdx = i;
			break;
		}	
	}
	if (decoderIdx != -1) {
		mStreamDecoder[decoderIdx].MP3_Load(stream);
		mStreamDecoder[decoderIdx].MP3_Play();
		mOccupiedMask[decoderIdx] = 1;
	}
	else {
		// TODO: I have to retire one decoder..
	}
}

void KAudioChannel::TimeElapse(unsigned int ms)
{
	for (int i = 0; i < MAX_SOFT_MIX_PER_CH; ++i) {
		if (mOccupiedMask[i] && !mStreamDecoder[i].IsPlaying()) {
			mStreamDecoder[i].MP3_Free();
			mOccupiedMask[i] = 0;
		}
	}
}
